package com.ozo.boot.auth.controller;

import cn.dev33.satoken.annotation.SaIgnore;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.ozo.boot.auth.constant.RedisKeyConstant;
import com.ozo.boot.auth.constant.SmsTypeEnum;
import com.ozo.boot.auth.exception.AuthExceptionEnum;
import com.ozo.boot.auth.log.LoginLogEvent;
import com.ozo.boot.auth.log.LoginLogUtil;
import com.ozo.boot.auth.pojo.dto.LoginDTO;
import com.ozo.boot.auth.pojo.dto.RefreshTokenDTO;
import com.ozo.boot.auth.pojo.dto.RegisterDTO;
import com.ozo.boot.auth.token.LoginType;
import com.ozo.boot.auth.token.TokenStore;
import com.ozo.boot.auth.pojo.vo.CaptchaVO;
import com.ozo.boot.auth.pojo.vo.RefreshTokenVO;
import com.ozo.boot.common.base.model.LoginUser;
import com.ozo.boot.common.base.model.ResultVO;
import com.ozo.boot.common.core.exception.ServiceException;
import com.ozo.boot.common.satoken.context.UserLoginHelper;
import com.ozo.boot.common.sms.service.SmsService;
import com.ozo.boot.manage.pojo.vo.SysUserInfoVO;
import com.ozo.boot.manage.service.ISysUserService;
import com.wf.captcha.GifCaptcha;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.LinkedHashMap;

/**
 * <p>
 * 认证 前端控制器
 * </p>
 *
 * @author qiangesoft
 * @since 2024-07-11
 */
@Tag(name = "认证管理")
@RequiredArgsConstructor
@RequestMapping("/auth")
@RestController
public class AuthController {

    private final SmsService smsService;

    private final TokenStore tokenStore;

    private final ISysUserService sysUserService;

    private final RedisTemplate<String, String> redisTemplate;

    @SaIgnore
    @Operation(summary = "验证码")
    @GetMapping("/captcha")
    public ResultVO<CaptchaVO> captcha() {
        GifCaptcha gifCaptcha = new GifCaptcha(120, 40);
        gifCaptcha.setLen(4);
        int onlyNumber = 2;
        gifCaptcha.setCharType(onlyNumber);

        String base64 = gifCaptcha.toBase64();
        String text = gifCaptcha.text();
        String uuid = IdUtil.simpleUUID();

        CaptchaVO captchaVO = new CaptchaVO();
        captchaVO.setCaptchaBase64(base64);
        captchaVO.setCaptchaUUID(uuid);

        redisTemplate.opsForValue().set(RedisKeyConstant.CAPTCHA + uuid, text);

        return ResultVO.success(captchaVO);
    }

    @SaIgnore
    @Operation(summary = "登录")
    @PostMapping("/login")
    public ResultVO<LoginUser> login(@RequestBody @Validated(value = LoginDTO.AccountLogin.class) LoginDTO loginDTO) {
        loginDTO.setGrantType(LoginType.PASSWORD);
        return ResultVO.success(tokenStore.login(loginDTO));
    }

    @SaIgnore
    @Operation(summary = "短信验证码")
    @PostMapping("/sms")
    public ResultVO<Void> sms(@RequestBody @Validated(value = LoginDTO.SendSms.class) LoginDTO loginDTO) {
        String mobile = loginDTO.getMobile();
        String code = String.valueOf(RandomUtil.randomInt(1000, 9999));

        LinkedHashMap<String, String> param = new LinkedHashMap<>();
        param.put("code", code);
        boolean success = smsService.sendMessage(SmsFactory.getSmsBlend().getConfigId(), mobile, param);
        if (!success) {
            return ResultVO.fail(AuthExceptionEnum.SMS_SEND_FAIL);
        }

        SmsTypeEnum smsType = loginDTO.getSmsType();
        if (SmsTypeEnum.LOGIN.equals(smsType)) {
            redisTemplate.opsForValue().set(RedisKeyConstant.SMS_LOGIN + mobile, code);
        } else {
            redisTemplate.opsForValue().set(RedisKeyConstant.SMS_REGISTER + mobile, code);
        }

        return ResultVO.success();
    }

    @SaIgnore
    @Operation(summary = "手机号登录")
    @PostMapping("/mobileLogin")
    public ResultVO<LoginUser> mobileLogin(@RequestBody @Validated(value = LoginDTO.PhoneLogin.class) LoginDTO loginDTO) {
        loginDTO.setGrantType(LoginType.PHONENUM);
        return ResultVO.success(tokenStore.login(loginDTO));
    }

    @SaIgnore
    @Operation(summary = "注册")
    @PostMapping("/register")
    public ResultVO<Void> register(@RequestBody @Valid RegisterDTO registerDTO) {
        String code = redisTemplate.opsForValue().get(RedisKeyConstant.SMS_REGISTER + registerDTO.getUsername());
        if (code != null) {
            redisTemplate.delete(RedisKeyConstant.SMS_REGISTER + registerDTO.getUsername());
        }
        if (!registerDTO.getSmsCode().equals(code)) {
            throw new ServiceException(AuthExceptionEnum.SMS_CODE_ERROR);
        }

        sysUserService.register(registerDTO.getUsername(), registerDTO.getPassword(), registerDTO.getNickname());

        return ResultVO.success();
    }

    @Operation(summary = "退出登录")
    @PostMapping("/logout")
    public ResultVO<Void> logout() {
        String username = UserLoginHelper.getUsername();
        tokenStore.logout();
        SpringUtil.publishEvent(new LoginLogEvent(LoginLogUtil.fail(username, JSONUtil.toJsonStr(ResultVO.success()))));
        return ResultVO.success();
    }

    @SaIgnore
    @Operation(summary = "刷新token")
    @PostMapping("/refreshToken")
    public ResultVO<RefreshTokenVO> refreshToken(@RequestBody RefreshTokenDTO refreshTokenDTO) {
        return ResultVO.success(tokenStore.refreshToken(refreshTokenDTO.getRefreshToken()));
    }

    @Operation(summary = "登录人信息")
    @GetMapping("/detail")
    public ResultVO<SysUserInfoVO> detail() {
        return ResultVO.success(sysUserService.getByUserId(UserLoginHelper.getUserId()));
    }

}
